{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 实例15：用Python批量转换doc文件为docx文件"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "python-docx模块虽然强大，但却不能处理后缀为\".doc\"的word文件。如果强制读取doc文件，将会报如下错误。\n",
    "![](images\\error.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "file 'data/公司001合同.doc' is not a Word file, content type is 'application/vnd.openxmlformats-officedocument.themeManager+xml'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-3-4a254209689d>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mdocx\u001b[0m \u001b[1;31m#导入docx库\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mdoc\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mdocx\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mDocument\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"data/公司001合同.doc\"\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m#打开word文件\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      3\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mpara\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mdoc\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mparagraphs\u001b[0m\u001b[1;33m:\u001b[0m \u001b[1;31m#读取word中的每个段落\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      4\u001b[0m     \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mpara\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtext\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32md:\\python\\lib\\site-packages\\docx\\api.py\u001b[0m in \u001b[0;36mDocument\u001b[1;34m(docx)\u001b[0m\n\u001b[0;32m     26\u001b[0m     \u001b[1;32mif\u001b[0m \u001b[0mdocument_part\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcontent_type\u001b[0m \u001b[1;33m!=\u001b[0m \u001b[0mCT\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mWML_DOCUMENT_MAIN\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     27\u001b[0m         \u001b[0mtmpl\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;34m\"file '%s' is not a Word file, content type is '%s'\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 28\u001b[1;33m         \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtmpl\u001b[0m \u001b[1;33m%\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0mdocx\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mdocument_part\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcontent_type\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     29\u001b[0m     \u001b[1;32mreturn\u001b[0m \u001b[0mdocument_part\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdocument\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     30\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mValueError\u001b[0m: file 'data/公司001合同.doc' is not a Word file, content type is 'application/vnd.openxmlformats-officedocument.themeManager+xml'"
     ]
    }
   ],
   "source": [
    "import docx #导入docx库\n",
    "doc = docx.Document(\"data/公司001合同.doc\") #打开word文件\n",
    "for para in doc.paragraphs: #读取word中的每个段落\n",
    "    print(para.text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "此时需要将“.doc”文件另存为“.docx”文件。然后就可以让python-docx模块尽情发挥了。手动另存为，需要逐个打开doc文件，然后点击“文件”->“另存为”，在弹出的“另存为”对话框中，将“保存类型”选择为“.docx”类型，然后保存。如果有100个文件，那得操作100次，很费时间。python提供了`win32com`模块（应安装pypiwin32模块，win32com包含在其中，即使用命令\"pip install pypiwin32\"），其中的`SaveAs`方法可以代替人手批量将文件另存为我们需要的格式。\n",
    "\n",
    "首先，我们使用`os`模块，获取需要做“另存为”操作的文件所在路径。由于后面的win32com模块在处理文件的时候，需要绝对路径，不然程序会报错说“找不到文件”。所以此处需要获取待处理文件的绝对路径，即从根目录开始的路径，注意文件夹层级使用两个反斜杠\"\\\\\"，其中一个斜杠是另一个的转义。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['C:\\\\Users\\\\dywei\\\\python\\\\python_excel\\\\Exp15.Convert doc to docx\\\\data\\\\公司001合同.doc',\n",
       " 'C:\\\\Users\\\\dywei\\\\python\\\\python_excel\\\\Exp15.Convert doc to docx\\\\data\\\\公司002合同.doc',\n",
       " 'C:\\\\Users\\\\dywei\\\\python\\\\python_excel\\\\Exp15.Convert doc to docx\\\\data\\\\公司003合同.doc',\n",
       " 'C:\\\\Users\\\\dywei\\\\python\\\\python_excel\\\\Exp15.Convert doc to docx\\\\data\\\\公司004合同.doc',\n",
       " 'C:\\\\Users\\\\dywei\\\\python\\\\python_excel\\\\Exp15.Convert doc to docx\\\\data\\\\公司005合同.doc']"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import os #用于获取目标文件所在路径\n",
    "path=\"C:\\\\Users\\\\dywei\\\\python\\\\python_excel\\\\Exp15.Convert doc to docx\\\\data\\\\\" # 文件夹绝对路径\n",
    "files=[]\n",
    "for file in os.listdir(path):\n",
    "    if file.endswith(\".doc\"): #排除文件夹内的其它干扰文件，只获取\".doc\"后缀的word文件\n",
    "        files.append(path+file) \n",
    "files"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然后就可以导入win32com模块，使用`Dispatch(\"Word.Application\")`打开word应用程序，然后再逐个打开doc文件`doc = word.Documents.Open(file)`，使用`SaveAs`另存文件为\"docx\"后缀的文件，另存之后，关闭打开的doc文件，再处理下一个，直到结束。我们保存新文件的时候，还是保持到原文件路径，只是将后缀改为\".docx\"，`\"{}x\".format(file)`指将在原文件名的末尾加上“x”。如下代码直接拿来使用即可，就不用浪费时间去深究其内部函数的构造了。有用，能解决问题就是硬道理。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "完成！\n"
     ]
    }
   ],
   "source": [
    "from win32com import client as wc #导入模块\n",
    "word = wc.Dispatch(\"Word.Application\") # 打开word应用程序\n",
    "for file in files:\n",
    "    doc = word.Documents.Open(file) #打开word文件\n",
    "    doc.SaveAs(\"{}x\".format(file), 12)#另存为后缀为\".docx\"的文件，其中参数12指docx文件\n",
    "    doc.Close() #关闭原来word文件\n",
    "word.Quit()\n",
    "print(\"完成！\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "批量“另存为”的结果如下所示。若需要将docx文件另存为doc文件，也可用类似的方法。\n",
    "![](images\\result.png)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
